use std::future::{ready, Ready};

use actix_web::{
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use actix_web::http::header::AUTHORIZATION;
use chrono::Local;
use futures_util::future::LocalBoxFuture;
use log::{debug, info};

use common::error::MyError;

use crate::ctx::{get_auth_conf, get_token_auth_cache, validation_token};
use crate::state::{ApiAuthPathConfModel, TokenAuthModel};

pub struct Auth;

impl<S, B> Transform<S, ServiceRequest> for Auth
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = AuthMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(AuthMiddleware { service }))
    }
}

pub struct AuthMiddleware<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // 进行鉴权操作，判断是否有权限
        let start_time = Local::now().timestamp_millis();
        debug!(">>>>>>>> start check auth");
        let result = has_permission(&req);
        let end_time = Local::now().timestamp_millis();
        debug!(
            ">>>>>>>> end check auth , times {} ms",
            end_time - start_time
        );
        match result {
            Ok(result) => {
                if result {
                    // 有权限，继续执行后续中间件
                    let fut = self.service.call(req);
                    Box::pin(async move {
                        let res = fut.await?;
                        Ok(res)
                    })
                } else {
                    // 没有权限，立即返回响应
                    Box::pin(async move {
                        // 鉴权失败，返回未授权的响应，停止后续中间件的调用
                        Err(Error::from(MyError::UnauthorizedError(
                            "无权访问".to_string(),
                        )))
                    })
                }
            }
            Err(error) => {
                // 没有权限，立即返回响应
                Box::pin(async move {
                    // 鉴权失败，返回未授权的响应，停止后续中间件的调用
                    Err(Error::from(error))
                })
            }
        }
    }
}

fn has_permission(req: &ServiceRequest) -> Result<bool, MyError> {
    //校验是否为api开头需要授权的接口
    let request_path = req.path();
    info!("request path:{:?}", request_path);
    if request_path.starts_with("/api") {
        // 校验token
        let token = req
            .headers()
            .get(AUTHORIZATION)
            .ok_or(MyError::UnauthorizedError("未携带授权凭证".to_string()))?
            .to_str()
            .or_else(|_| Err(MyError::UnauthorizedError("未携带授权凭证".to_string())))?;
        validation_token(token)?;
        //获取鉴权配置 只读
        let auth_conf = get_auth_conf()?;
        let token_auth_cache = get_token_auth_cache(&token)?;
        //先根据请求方法分流
        let request_method = req.method().to_string();
        info!("request method:{:?}", request_method);
        let method_conf = &auth_conf.method;
        if let Some(method_conf) = method_conf {
            //如果存在根据请求方法鉴权的规则就尝试解析对应方法下的规则
            let api_auth_path_conf_model = method_conf.get(&request_method);
            //路径匹配
            api_path_check(request_path, api_auth_path_conf_model, &token_auth_cache)?;
        } else {
            //如果没有根据请求方法鉴权的规则就遍历所有规则
            if let Some(empty_method_conf) = &auth_conf.empty_method {
                //路径匹配
                let api_auth_path_conf_model = empty_method_conf.get(&request_method);
                if let Some(api_auth_path_conf_model) = api_auth_path_conf_model {
                    //auth_key校验
                    api_path_auth_key_check(
                        api_auth_path_conf_model.auth_key.clone(),
                        &token_auth_cache,
                    )?;
                    //role校验码
                    api_path_role_check(api_auth_path_conf_model.roles.clone(), &token_auth_cache)?;
                }
            }
        }
        Ok(true)
    } else {
        Ok(true)
    }
}

fn api_path_check(
    request_path: &str,
    api_auth_path_conf_model: Option<&ApiAuthPathConfModel>,
    token_auth_cache: &TokenAuthModel,
) -> Result<bool, MyError> {
    if let Some(api_auth_path_conf_model) = api_auth_path_conf_model {
        if let Some(api_path_conf) = &api_auth_path_conf_model.api_path {
            let api_auth_key_and_role_conf_model = api_path_conf.get(request_path);
            if let Some(api_auth_key_and_role_conf_model) = api_auth_key_and_role_conf_model {
                //auth_key校验
                api_path_auth_key_check(
                    api_auth_key_and_role_conf_model.auth_key.clone(),
                    &token_auth_cache,
                )?;
                //role校验码
                api_path_role_check(
                    api_auth_key_and_role_conf_model.roles.clone(),
                    &token_auth_cache.clone(),
                )?;
                Ok(true)
            } else {
                //没有匹配到
                Ok(false)
            }
        } else {
            //没有匹配到
            Ok(false)
        }
    } else {
        //没有匹配到
        Ok(false)
    }
}

fn api_path_role_check(
    roles: Option<Vec<String>>,
    token_auth_cache: &TokenAuthModel,
) -> Result<bool, MyError> {
    if let Some(roles) = roles {
        let token_roles = &token_auth_cache
            .role
            .clone()
            .ok_or(MyError::UnauthorizedError("未授任何角色".to_string()))?;
        for role in &roles {
            if token_roles.contains(&role) {
                return Ok(true);
            }
        }
        return Err(MyError::UnauthorizedError(format!(
            "需要以下任一角色[{:?}] ",
            &roles
        )));
    } else {
        Ok(true)
    }
}

fn api_path_auth_key_check(
    auth_key: Option<String>,
    token_auth_cache: &TokenAuthModel,
) -> Result<bool, MyError> {
    if let Some(auth_key) = auth_key {
        let token_auth_key = &token_auth_cache
            .auth_key
            .clone()
            .ok_or(MyError::UnauthorizedError("未获得任何授权".to_string()))?;
        if token_auth_key.contains(&auth_key) {
            //校验通过
            Ok(true)
        } else {
            Err(MyError::UnauthorizedError(format!(
                "无此权限[{:?}]",
                &auth_key
            )))
        }
    } else {
        //没有匹配到
        Ok(false)
    }
}
